//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.UIDesigner
{
    using System;
    using System.Collections.Generic;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using Microsoft.Research.ScientificWorkflow.UIDesigner;
    using Microsoft.Research.DataLayer;
    using SR = Microsoft.Research.DataLayer;
    using Microsoft.Research.ScientificWorkflow.TridentModel;
    using System.Resources;
    using System.ComponentModel;
    using System.Collections.ObjectModel;
    using Microsoft.Research.ScientificWorkflow.TridentComposer;
    using TridentAPI;
    using Microsoft.Research.eResearch.Common.Eventing;
    using System.Linq;

    /// <summary>
    /// Interaction logic for ComposerControl.xaml
    /// </summary>
    public partial class ComposerControl : UserControl
    {
        #region Private data.

        /// <summary>
        /// True if panning is in progress.
        /// </summary>
        private bool panDragging;

        /// <summary>
        /// Reference point for panning.
        /// </summary>
        private Point referencePoint;

        /// <summary>
        /// Custom cursor for panning.
        /// </summary>
        private Cursor customOpenHandCursor;

        /// <summary>
        /// Closed hand cursor.
        /// </summary>
        private Cursor customClosedHandCursor;

        /// <summary>
        /// This is used to access registry connection.
        /// </summary>
        private SR.Connection registryConnection;

        /// <summary>
        /// The application's main window.
        /// </summary>
        private Window mainWindow;

        /// <summary>
        /// The helper class for the composer control.
        /// </summary>
        private ComposerRenderer composerRenderer;

        /// <summary>
        /// Condition data store.
        /// </summary>
        private ConditionDataStore conditionStore = new ConditionDataStore();

        /// <summary>
        /// The datatypes present.
        /// </summary>
        private Dictionary<String, DataTypeModel> dataTypes = new Dictionary<string, DataTypeModel>();

        #endregion Private data.

        #region Public properties.

        /// <summary>
        /// Using a DependencyProperty as the backing store for CurrentRenderMode.
        /// </summary>
        internal static System.Windows.DependencyProperty CurrentRenderModeProperty =
            System.Windows.DependencyProperty.RegisterAttached("CurrentRenderMode",
            typeof(RenderMode),
            typeof(ComposerControl),
            new System.Windows.FrameworkPropertyMetadata(
                RenderMode.None,
                FrameworkPropertyMetadataOptions.Inherits,
                new PropertyChangedCallback(ComposerControl.OnRenderModeChanged)
                )
            );

        /// <summary>
        /// Using a DependencyProperty as the backing store for Current composer name.
        /// </summary>
        internal static System.Windows.DependencyProperty ComposerControlNameProperty =
           System.Windows.DependencyProperty.RegisterAttached("ComposerControlName",
           typeof(string),
           typeof(ComposerControl),
           new System.Windows.FrameworkPropertyMetadata(
               string.Empty,
               FrameworkPropertyMetadataOptions.Inherits
               )
           );

        /// <summary>
        /// Specified if the user has selected panning or no.
        /// </summary>
        public bool PanEnabled
        {
            get;
            set;
        }

        /// <summary>
        /// Gets and sets the current render mode.
        /// </summary>
        public RenderMode CurrentRenderMode
        {
            get { return (RenderMode)GetValue(CurrentRenderModeProperty); }
            private set { SetValue(CurrentRenderModeProperty, value); }
        }

        /// <summary>
        /// Composer control name.
        /// </summary>
        public string ComposerControlName
        {
            get { return (string)GetValue(ComposerControlNameProperty); }
            set { SetValue(ComposerControlNameProperty, value); }
        }

        /// <summary>
        /// Gets the PositionableElementsCanvas.
        /// </summary>
        public Canvas PositionableElementsCanvas
        {
            get
            {
                return this.positionableElementsCanvas;
            }
        }

        /// <summary>
        /// Gets the jobname textbox.
        /// </summary>
        public TextBoxWatermark TxtJobName
        {
            get
            {
                return this.txtJobName;
            }
        }

        /// <summary>
        /// The workflowspace grid.
        /// </summary>
        public Grid WorkflowSpaceGrid
        {
            get
            {
                return this.WorkflowSpace;
            }
        }

        /// <summary>
        /// Gets the collection of currently selected elements.
        /// </summary>
        public SelectedElementsCollection CurrentSelectedElements
        {
            get
            {
                return this.composerRenderer.CurrentSelectedElements;
            }
        }

        /// <summary>
        /// Gets or stes the SelectedParamConnections.
        /// </summary>
        internal Collection<ParameterConnectionElement> SelectedParamConnections
        {
            get
            {
                return this.composerRenderer.SelectedParamConnections;
            }
        }

        public Dictionary<string, ActivityParameterDesigner> InputParameterElements
        {
            get
            {
                return this.composerRenderer.InputParameterElements;
            }
        }

        public Dictionary<string, ActivityParameterDesigner> OutputParameterElements
        {
            get
            {
                return this.composerRenderer.OutputParameterElements;
            }
        }

        /// <summary>
        /// The condition data store for the if else activity.
        /// </summary>
        public ConditionDataStore ConditionStore
        {
            get { return conditionStore; }
            set { conditionStore = value; }
        }

        /// <summary>
        /// This spcifies whether the Current Workflow is commited to Primary Source.
        ///     TRUE = Changes in the wf is commited to Primary Source.
        ///     FALSE = Changes in the wf is NOT commited to Primary Source.
        /// </summary>
        public bool IsWFCommitted
        {
            get
            {
                return this.composerRenderer.IsWFCommitted;
            }
            set
            {
                this.composerRenderer.IsWFCommitted = value;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public TridentWorkflowModel CurrentWorkflowModel
        {
            get
            {
                return this.composerRenderer.CurrentWorkflowModel;
            }
            set
            {
                this.composerRenderer.CurrentWorkflowModel = value;
            }
        }

        /// <summary>
        /// The workflow composer Instance.
        /// </summary>
        public WorkflowComposer WorkflowComposer
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the uiElements.
        /// </summary>
        public Dictionary<string, ITridentUIElement> UIElements
        {
            get
            {
                return this.composerRenderer.UIElements;
            }
        }

        /// <summary>
        /// Gets the uiElements.
        /// </summary>
        public Dictionary<string, DataTypeModel> DataTypes
        {
            get
            {
                return this.composerRenderer.DataTypes;
            }
        }

        /// <summary>
        /// The current composer renderer.
        /// </summary>
        public ComposerRenderer CurrentComposerRenderHelper
        {
            get
            {
                return this.composerRenderer;
            }
        }

        /// <summary>
        /// Gets the selected node.
        /// </summary>
        public Machine SelectedNode
        {
            get
            {
                Machine machine = null;
                ComboBoxItem selectedItem = this.MachineList.SelectedItem as ComboBoxItem;
                if (selectedItem != null)
                {
                    machine = selectedItem.Tag as Machine;
                }

                return machine;
            }
        }

        #endregion Public properties.

        #region Events.

        /// <summary>
        /// Fired on execute of the workflow.
        /// </summary>
        public event EventHandler<ExecuteEventArgs> OnExecute;

        /// <summary>
        /// Fired on terminate of the workflow.
        /// </summary>
        public event EventHandler OnTerminate;

        /// <summary>
        /// Fired when the user wants to close the workflow.
        /// </summary>
        public event EventHandler OnCloseWorkflow;

        /// <summary>
        /// Fired when the user clicks on the conetxt menu items.
        /// </summary>
        public event EventHandler<ProvenanceModeEventArgs> ProvenanceMenuClick;

        #endregion Events.

        #region Constructor.

        /// <summary>
        /// Create a new instance of the composer control.
        /// </summary>
        /// <param name="connection">The connection object.</param>
        /// <param name="wfComposer">The workflow composer.</param>
        /// <param name="dataTypes">The datatypes.</param>
        /// <param name="renderMode">The mode.</param>
        public ComposerControl(SR.Connection connection, WorkflowComposer workflowComposer, Dictionary<String, DataTypeModel> dataTypes, RenderMode renderMode)
        {
            InitializeComponent();
            this.CurrentRenderMode = renderMode;
            DependencyPropertyDescriptor propDescriptor = DependencyPropertyDescriptor.FromProperty(NameProperty, typeof(ComposerControl));
            propDescriptor.AddValueChanged(this, OnNameChanged);
            this.WorkflowComposer = workflowComposer;
            this.registryConnection = connection;

            this.mainWindow = Application.Current.MainWindow;

            this.dataTypes = dataTypes;
            this.composerRenderer = new ComposerRenderer(this, this.registryConnection, dataTypes, this.conditionStore);
            this.composerRenderer.SelectedActivitiesChanged += this.OnSelectedActivitiesChanged;

            this.DisableToolbar();
            this.AttachPanHandlers();

            // Set the cursors for the Pan functionality.
            this.customOpenHandCursor = CursorUtilities.GetCursor(@"Images\ScientificWorkflow\openhand.cur");

            this.customClosedHandCursor = CursorUtilities.GetCursor(@"Images\ScientificWorkflow\closedhand.cur");

            if (renderMode == RenderMode.ProvenanceMode)
            {
                this.composerRenderer.ProvenanceMenuClick += new EventHandler<ProvenanceModeEventArgs>(OnProvenanceMenuClick);
            }

            this.Loaded += this.OnLoaded;
        }

        /// <summary>
        /// Create a new instance of the composer control.
        /// </summary>
        /// <param name="connection">The connection object.</param>
        /// <param name="wfComposer">The workflow composer.</param>
        /// <param name="dataTypes">The datatypes.</param>
        /// <param name="renderMode">The mode.</param>
        /// <param name="owner">The owner window.</param>
        public ComposerControl(SR.Connection connection, WorkflowComposer workflowComposer, Dictionary<String, DataTypeModel> dataTypes, RenderMode renderMode, Window owner)
            : this(connection, workflowComposer, dataTypes, renderMode)
        {
            if (owner == null)
            {
                throw new ArgumentNullException("owner");
            }
            this.mainWindow = owner;
        }

        #endregion Constructor.

        #region Public Methods

        /// <summary>
        /// Renders the workflow from the Activity Element to UI.
        /// </summary>
        /// <param name="wfModel">
        /// WorkflowModel
        /// </param>
        /// <param name="isRefresh">
        /// To be refreshed or not.
        /// </param>
        public void LoadWorkflow(TridentWorkflowModel workflowModel, bool isRefresh)
        {
            this.composerRenderer.LoadWorkFlow(workflowModel, isRefresh);
            this.SelectedParamConnections.Clear();
            this.EnableToolbar();
        }

        /// <summary>
        /// Reinitialize the swap handlers.
        /// </summary>
        public void InitializeSubsectionHandlers(TridentWorkflowModel workflowModel)
        {
            foreach (string key in workflowModel.ModelsHashtable.Keys)
            {
                BaseModel model = workflowModel.ModelsHashtable[key] as BaseModel;
                if (model != null && model.IsSubsectionModel)
                {
                    (model as TridentSubsectionModel).SwapSubsectionState += this.OnSubsectionSwapState;
                }
            }
        }

        /// <summary>
        /// Clears the UI cache.
        /// </summary>
        public void ClearUICache()
        {
            this.composerRenderer.ClearUICache();
        }

        /// <summary>
        /// Get rid of the property pane.
        /// </summary>
        public void RemovePropertyPaneOnChange()
        {
            this.composerRenderer.RemovePropertyPaneOnChange();
        }

        /// <summary>
        /// Show the properties pane.
        /// </summary>
        public void ShowProperties()
        {
            this.composerRenderer.ShowProperties();
        }

        /// <summary>
        /// Save the conditional activities.
        /// </summary>
        /// <returns>
        /// Status of the save.
        /// </returns>
        public bool SaveConditionalActivities()
        {
            MessageBoxResult result = MessageBoxResult.None;
            if (conditionStore != null)
            {
                Collection<ConditionConnector> connectorList = this.conditionStore.FetchConditionalConnectorList();
                foreach (ConditionConnector connector in connectorList)
                {
                    if (!connector.WellFormedTreeExists)
                    {
                        TridentMessageBox.ShowTridentErrorMessageBox(Application.Current.MainWindow,
                            TridentResourceManager.GetString("NoWellFormedTree"));
                        return false;
                    }

                    Collection<SimpleActivityModel> orphanList = connector.CheckForOrphanElements();
                    if (orphanList.Count > 0 && result == MessageBoxResult.None)
                    {
                        result = TridentMessageBox.ShowTridentConfirmationYesNoMessageBox(
                            TridentResourceManager.GetString("OrphanActivitiesPresent"));
                        if (result == MessageBoxResult.No)
                        {
                            return false;
                        }
                    }
                    if (result == MessageBoxResult.Yes || result == MessageBoxResult.None)
                    {
                        connector.OnSave(orphanList);
                    }
                }
            }
            this.RefreshUI();
            return true;
        }

        /// <summary>
        /// Show the properties pane.
        /// </summary>
        /// <param name="modelToShowPropertiesOf">
        /// The model to show the properties of.
        /// </param>
        public void ShowProperties(BaseModel modelToShowPropertiesOf)
        {
            this.composerRenderer.ShowProperties(modelToShowPropertiesOf);
        }

        /// <summary>
        /// This function is used to toggle all parameter Bindings in current workflow.
        /// </summary>
        /// <param name="changeState">Visibilty of the parameter bindings of the Activity.</param>
        public void ToggleAllParameterBindings(Visibility changeState)
        {
            this.composerRenderer.ToggleAllParameterBindings(changeState);
        }

        /// <summary>
        /// Swaps the state of the subsection.
        /// </summary>
        /// <param name="sender">
        /// Subsection.
        /// </param>
        public void SwapSubsectionState(object sender)
        {
            this.composerRenderer.SwapSubsectionState(sender);
        }

        /// <summary>
        /// Deletes the activity.
        /// </summary>
        /// <param name="sender">
        /// Composer.
        /// </param>
        /// <param name="e">
        /// DeleteElementEventArgs.
        /// </param>
        private void OnDeleteActivity(object sender, EventArgs e)
        {
            this.DeleteActivity();
        }

        /// <summary>
        /// Activity Delete.
        /// </summary>
        public void DeleteActivity()
        {
            this.composerRenderer.OnTridentElemDelete(this, new EventArgs());
        }

        /// <summary>
        /// fired when the provenance context menu click is raised.
        /// </summary>
        /// <param name="sender">
        /// ITridentUIElement
        /// </param>
        /// <param name="e">
        /// ProvenanceModeEventArgs
        /// </param>
        internal void OnProvenanceMenuClick(object sender, ProvenanceModeEventArgs e)
        {
            this.ProvenanceMenuClick.Fire(sender, e);
        }

        #endregion Public Methods

        #region Private Methods.
        
        ///// <summary>
        ///// Swap the subsection state.
        ///// </summary>
        ///// <param name="sender">
        ///// Subsection.
        ///// </param>
        ///// <param name="e">
        ///// EventArgs.
        ///// </param>
        //private void SwapSubsection(object sender, EventArgs e)
        //{
        //    this.SwapSubsectionState(sender);
        //}

        /// <summary>
        /// The name changed property callback. Changes the dependency property "ComposerControlName" on change of the name.
        /// </summary>
        /// <param name="sender">source</param>
        /// <param name="e">event arguments</param>
        private void OnNameChanged(object sender, EventArgs e)
        {
            this.SetValue(ComposerControl.ComposerControlNameProperty, this.Name);
        }

        /// <summary>
        /// Fired when the render mode changes.
        /// </summary>
        /// <param name="d">
        /// The object on which the renderMode property is set.
        /// </param>
        /// <param name="e">
        /// DependencyPropertyChangedEventArgs.
        /// </param>
        private static void OnRenderModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ComposerControl compCtrl = d as ComposerControl;
            RenderMode renderModeToSet = (RenderMode)e.NewValue;
            if (compCtrl != null)
            {
                if (RenderMode.ComposerMode == renderModeToSet)
                {
                    compCtrl.dckComposerCtrls.Visibility = Visibility.Visible;
                    compCtrl.terminatebutton.Visibility = Visibility.Visible;
                    compCtrl.pausebutton.Visibility = Visibility.Visible;
                    compCtrl.seperator1.Visibility = Visibility.Visible;
                    compCtrl.btnProperties.Visibility = Visibility.Visible;
                    compCtrl.btnCloseWorkFlow.Visibility = Visibility.Visible;

                    // Hide Terminate and Pause controls.
                    compCtrl.dckMonitorCtrls.Visibility = Visibility.Collapsed;

                    compCtrl.grdSplitterComposerPropPane.Visibility = Visibility.Visible;
                }
                else if (RenderMode.MonitorMode == renderModeToSet)
                {
                    // Show Terminate and Pause controls.
                    compCtrl.dckMonitorCtrls.Visibility = Visibility.Visible;

                    compCtrl.dckComposerCtrls.Visibility = Visibility.Collapsed;
                    compCtrl.btnProperties.Visibility = Visibility.Collapsed;
                    compCtrl.btnCloseWorkFlow.Visibility = Visibility.Collapsed;
                    compCtrl.grdSplitterComposerPropPane.Visibility = Visibility.Collapsed;
                }
                else if (RenderMode.VisualizationMode == renderModeToSet)
                {
                    // Hide Terminate and Pause controls.
                    compCtrl.dckMonitorCtrls.Visibility = Visibility.Collapsed;

                    compCtrl.dckComposerCtrls.Visibility = Visibility.Collapsed;
                    compCtrl.btnProperties.Visibility = Visibility.Collapsed;
                    compCtrl.btnCloseWorkFlow.Visibility = Visibility.Collapsed;

                    compCtrl.seperator1.Visibility = Visibility.Collapsed;
                    compCtrl.grdSplitterComposerPropPane.Visibility = Visibility.Collapsed;
                }
                else if (RenderMode.ProvenanceMode == renderModeToSet)
                {
                    // Hide Terminate and Pause controls.
                    compCtrl.dckMonitorCtrls.Visibility = Visibility.Collapsed;

                    compCtrl.dckComposerCtrls.Visibility = Visibility.Collapsed;
                    compCtrl.btnProperties.Visibility = Visibility.Collapsed;
                    compCtrl.btnCloseWorkFlow.Visibility = Visibility.Collapsed;

                    compCtrl.seperator1.Visibility = Visibility.Collapsed;
                    compCtrl.grdSplitterComposerPropPane.Visibility = Visibility.Collapsed;
                }
            }
        }

        /// <summary>
        /// Fired on loaded.
        /// </summary>
        /// <param name="sender">
        /// This.
        /// </param>
        /// <param name="e">
        /// RoutedEventArgs.
        /// </param>
        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            this.InitializeDataTypeWindow();
        }

        /// <summary>
        /// Fired when the selected activity on the composer changes.
        /// </summary>
        /// <param name="sender">
        /// UiHelper.
        /// </param>
        /// <param name="e">
        /// EventArgs.
        /// </param>
        private void OnSelectedActivitiesChanged(object sender, EventArgs e)
        {
            if (this.composerRenderer.CurrentSelectedElements.Count > 0)
            {
                this.btnProperties.IsEnabled = true;
                this.btnProperties.Opacity = 1.0;
            }
            else
            {
                this.btnProperties.IsEnabled = false;
                this.btnProperties.Opacity = 0.3;
            }
        }

        /// <summary>
        /// Remove the parameter selections.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.composerRenderer.RemoveParameterSelection();
        }

        /// <summary>
        /// Event handler for Execute Button Click
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">RoutedEventArgs</param>
        private void OnExecuteWorkflow(object sender, RoutedEventArgs e)
        {
            if (this.OnExecute != null)
            {
                string jobName = string.Empty;
                if (!this.txtJobName.IsWatermarked)
                {
                    jobName = this.txtJobName.Text;
                }

                ExecuteEventArgs executeEventArgs = new ExecuteEventArgs(jobName, (this.MachineList.SelectedItem as ComboBoxItem).Tag, false);
                this.OnExecute.Invoke(this, executeEventArgs);
                this.txtJobName.ClearText();
            }
        }

        /// <summary>
        /// Event handler for Debug Button Click
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">RoutedEventArgs</param>
        private void OnDebugWorkflow(object sender, RoutedEventArgs e)
        {
            if (this.OnExecute != null)
            {
                string jobName = string.Empty;
                if (!this.txtJobName.IsWatermarked)
                {
                    jobName = this.txtJobName.Text;
                }

                ExecuteEventArgs executeEventArgs = new ExecuteEventArgs(jobName, (this.MachineList.SelectedItem as ComboBoxItem).Tag, true);
                this.OnExecute.Invoke(this, executeEventArgs);
                this.txtJobName.ClearText();
            }
        }

        /// <summary>
        /// Handles the zooming of the workflow area.
        /// </summary>
        /// <param name="sender">
        /// Slider control.
        /// </param>
        /// <param name="e">
        /// RoutedPropertyChangedEventArgs<double>.
        /// </param>
        private void ZoomWorkflow(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            this.ResizeWorkspace(e.NewValue / 100, e.NewValue / 100);
        }

        /// <summary>
        /// Resize the workspace by applying transforms to the workspace area.
        /// </summary>
        /// <param name="xChange">
        /// The x-axis change.
        /// </param>
        /// <param name="yChange">
        /// The y-axis change.
        /// </param>
        private void ResizeWorkspace(double xChange, double yChange)
        {
            if (this.grdScrlHolder != null)
            {
                TransformGroup transGroup = this.grdScrlHolder.LayoutTransform as TransformGroup;
                if (transGroup != null && transGroup.Children.Count > 0)
                {
                    ScaleTransform scale = transGroup.Children[0] as ScaleTransform;
                    if (scale != null)
                    {
                        scale.ScaleX = xChange;
                        scale.ScaleY = yChange;
                    }
                }

                this.UpdateLayout();
                this.RepositionLines();
            }
        }

        /// <summary>
        /// Resets the zooming that has happened.
        /// </summary>
        public void ResetTransforms()
        {
            if (this.grdScrlHolder != null)
            {
                TransformGroup transGroup = this.grdScrlHolder.LayoutTransform as TransformGroup;
                if (transGroup != null && transGroup.Children.Count > 1)
                {
                    ScaleTransform scale = transGroup.Children[0] as ScaleTransform;
                    if (scale != null)
                    {
                        scale.ScaleX = 1;
                        scale.ScaleY = 1;
                    }

                    TranslateTransform trans = transGroup.Children[1] as TranslateTransform;
                    if (trans != null)
                    {
                        trans.X = 0;
                        trans.Y = 0;
                    }

                    this.sliderCtrl.Value = 100;
                }

                this.UpdateLayout();
                //this.RepositionLines();
            }
        }

        /// <summary>
        /// Fired on click of the Zoom minus button.
        /// </summary>
        /// <param name="sender">
        /// Zoom minus button.
        /// </param>
        /// <param name="e">
        /// RoutedEventArgs.
        /// </param>
        private void OnZoomMinusClicked(object sender, RoutedEventArgs e)
        {
            this.sliderCtrl.Value -= 10;
        }

        /// <summary>
        /// Fired on click of the Zoom plus button.
        /// </summary>
        /// <param name="sender">
        /// Zoom plus button.
        /// </param>
        /// <param name="e">
        /// RoutedEventArgs.
        /// </param>
        private void OnZoomPlusClicked(object sender, RoutedEventArgs e)
        {
            this.sliderCtrl.Value += 10;
        }

        /// <summary>
        /// Fired on click of Pan.
        /// </summary>
        /// <param name="sender">
        /// Pan button.
        /// </param>
        /// <param name="e">
        /// RoutedEventArgs.
        /// </param>
        private void OnPanClicked(object sender, RoutedEventArgs e)
        {
            if ((bool)this.btnPan.IsChecked)
            {
                this.PanEnabled = true;
            }
            else
            {
                this.PanEnabled = false;
            }
        }

        /// <summary>
        /// Fired on click of Fit to screen.
        /// </summary>
        /// <param name="sender">
        /// Fit to screen button.
        /// </param>
        /// <param name="e">
        /// RoutedEventArgs.
        /// </param>
        private void OnFitToScreenClicked(object sender, RoutedEventArgs e)
        {
            double xCorrectionNeeded = 1;
            double yCorrectionNeeded = 1;
            double availWidth = this.scrlWfHolder.ActualWidth;
            double currentWidth = this.WorkflowSpace.ActualWidth;
            ScaleTransform scale = (this.grdScrlHolder.LayoutTransform as TransformGroup).Children[0] as ScaleTransform;
            if (currentWidth > availWidth || (currentWidth * scale.ScaleX) > availWidth)
            {
                xCorrectionNeeded = availWidth / currentWidth;
                yCorrectionNeeded = xCorrectionNeeded;
            }

            double availHeight = this.scrlWfHolder.ActualHeight;
            double currentHeight = this.WorkflowSpace.ActualHeight;
            if (currentHeight > availHeight || (currentHeight * scale.ScaleY) > availHeight)
            {
                if (yCorrectionNeeded > (availHeight / currentHeight))
                {
                    yCorrectionNeeded = availHeight / currentHeight;
                }

                if (xCorrectionNeeded > yCorrectionNeeded)
                {
                    xCorrectionNeeded = yCorrectionNeeded;
                }
            }

            if (xCorrectionNeeded != 1 || yCorrectionNeeded != 1)
            {
                this.sliderCtrl.Value = xCorrectionNeeded * 100;
                this.ResizeWorkspace(xCorrectionNeeded, yCorrectionNeeded);
            }
        }

        /// <summary>
        /// Fired on mouse move over the workflow area. Handles panning.
        /// </summary>
        /// <param name="sender">
        /// Workflow space.
        /// </param>
        /// <param name="e">
        /// MouseEventArgs.
        /// </param>
        private void OnWorkspacePreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (this.PanEnabled && this.panDragging && MouseButtonState.Pressed == e.LeftButton)
            {
                Point currentPoint = e.GetPosition(this);
                this.Cursor = this.customClosedHandCursor;

                // Dividing by a value so that we get a smoother scroll..
                double xDelta = (currentPoint.X - this.referencePoint.X);
                double yDelta = (currentPoint.Y - this.referencePoint.Y);
                double requiredHorizontalOffset = this.scrlWfHolder.HorizontalOffset - xDelta;
                double requiredVerticalOffset = this.scrlWfHolder.VerticalOffset - yDelta;
                this.scrlWfHolder.ScrollToHorizontalOffset(requiredHorizontalOffset);
                this.scrlWfHolder.ScrollToVerticalOffset(requiredVerticalOffset);
                this.referencePoint = currentPoint;
                e.Handled = true;
            }
        }

        /// <summary>
        /// Fired on left button up. Resets pan initialization.
        /// </summary>
        /// <param name="sender">
        /// Mouse left button.
        /// </param>
        /// <param name="e">
        /// MouseButtonEventArgs.
        /// </param>
        private void OnWorkspacePreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (this.PanEnabled)
            {
                this.panDragging = false;
                this.Cursor = this.customOpenHandCursor;
                e.Handled = true;
            }
        }

        /// <summary>
        /// Fired on Mouse down over the workflow space.
        /// </summary>
        /// <param name="sender">
        /// Workflow space.
        /// </param>
        /// <param name="e">
        /// MouseButtonEventArgs.
        /// </param>
        private void OnWorkspacePreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (this.PanEnabled)
            {
                this.panDragging = true;
                this.referencePoint = e.GetPosition(this);
                this.Cursor = this.customClosedHandCursor;
                e.Handled = true;
            }
        }

        /// <summary>
        /// Fired on Mouse leave from the workflow space.
        /// </summary>
        /// <param name="sender">
        /// Workflow space.
        /// </param>
        /// <param name="e">
        /// MouseEventArgs.
        /// </param>
        private void OnWorkspaceMouseLeave(object sender, MouseEventArgs e)
        {
            if (this.PanEnabled)
            {
                this.Cursor = Cursors.Arrow;
                e.Handled = true;
            }
        }

        /// <summary>
        /// Fired on mouse enter into the workflow space.
        /// </summary>
        /// <param name="sender">
        /// Workflow space.
        /// </param>
        /// <param name="e">
        /// MouseEventArgs.
        /// </param>
        private void OnWorkspaceMouseEnter(object sender, MouseEventArgs e)
        {
            if (this.PanEnabled)
            {
                this.Cursor = this.customOpenHandCursor;
                e.Handled = true;
            }
        }

        /// <summary>
        /// Fired when the user drags over a workflow.
        /// Checks if the scrollview has to be horizontally scrolled.
        /// </summary>
        /// <param name="sender">
        /// CentralPane grid.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnCentralPaneDragOver(object sender, DragEventArgs e)
        {
            e.Effects = DragDropEffects.None;
            Point point = SafeNativeMethods.GetMousePosition(this.grdCentralPane);
            if (point.X > this.scrlWfHolder.ViewportWidth - 200)
            {
                this.scrlWfHolder.ScrollToHorizontalOffset(this.scrlWfHolder.HorizontalOffset + 8);
            }
            else if (point.X < 200)
            {
                this.scrlWfHolder.ScrollToHorizontalOffset(this.scrlWfHolder.HorizontalOffset - 8);
            }

            if (point.Y > this.scrlWfHolder.ViewportHeight - 75)
            {
                this.scrlWfHolder.ScrollToVerticalOffset(this.scrlWfHolder.VerticalOffset + 8);
            }
            else if (point.Y < 75)
            {
                this.scrlWfHolder.ScrollToVerticalOffset(this.scrlWfHolder.VerticalOffset - 8);
            }
        }

        /// <summary>
        /// Attach the event handlers for the panning feature.
        /// </summary>
        private void AttachPanHandlers()
        {
            this.WorkflowSpace.MouseEnter += new MouseEventHandler(this.OnWorkspaceMouseEnter);
            this.WorkflowSpace.MouseLeave += new MouseEventHandler(this.OnWorkspaceMouseLeave);
            this.WorkflowSpace.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(this.OnWorkspacePreviewMouseLeftButtonDown);
            this.WorkflowSpace.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(this.OnWorkspacePreviewMouseLeftButtonUp);
            this.WorkflowSpace.PreviewMouseMove += new MouseEventHandler(this.OnWorkspacePreviewMouseMove);

            this.PositionableElementsCanvas.MouseEnter += new MouseEventHandler(this.OnWorkspaceMouseEnter);
            this.PositionableElementsCanvas.MouseLeave += new MouseEventHandler(this.OnWorkspaceMouseLeave);
            this.PositionableElementsCanvas.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(this.OnWorkspacePreviewMouseLeftButtonDown);
            this.PositionableElementsCanvas.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(this.OnWorkspacePreviewMouseLeftButtonUp);
            this.PositionableElementsCanvas.PreviewMouseMove += new MouseEventHandler(this.OnWorkspacePreviewMouseMove);

            this.grdCentralPane.AllowDrop = true;
            this.grdCentralPane.PreviewDragOver += new DragEventHandler(this.OnCentralPaneDragOver);
        }

        /// <summary>
        /// Enable the execution buttons.
        /// </summary>
        public void EnablePlayButton()
        {
            // Enable the Play Button.
            this.ExecuteButton.IsEnabled = true;
            this.ExecuteButton.Opacity = 1.0;

            // Enable the Debug Button.
            this.DebugButton.IsEnabled = true;
            this.DebugButton.Opacity = 1.0;

            this.pausebutton.IsEnabled = false;
            this.pausebutton.Opacity = 0.3;

            this.DisableTerminateButton();
        }

        /// <summary>
        /// Disable Terminate button.
        /// </summary>
        public void DisableTerminateButton()
        {
            this.terminatebutton.IsEnabled = false;
            this.terminatebutton.Opacity = 0.3;
        }

        /// <summary>
        /// Disable the execution buttons.
        /// </summary>
        public void DisablePlayButton()
        {
            // Disable the Play Button.
            this.ExecuteButton.IsEnabled = false;
            this.ExecuteButton.Opacity = 0.3;

            // Disable the Debug Button.
            this.DebugButton.IsEnabled = false;
            this.DebugButton.Opacity = 0.3;

            this.pausebutton.IsEnabled = false;
            this.pausebutton.Opacity = 0.3;
        }

        /// <summary>
        /// Enable terminate button.
        /// </summary>
        public void EnableTerminateButton()
        {
            this.terminatebutton.IsEnabled = true;
            this.terminatebutton.Opacity = 1.0;
        }

        /// <summary>
        /// Disables the buttons on the toolbar.
        /// </summary>
        private void DisableToolbar()
        {
            this.toolBar.IsEnabled = false;
            this.toolBar.Opacity = 0.3;
        }

        /// <summary>
        /// Enables the buttons on the toolbar.
        /// </summary>
        private void EnableToolbar()
        {
            this.toolBar.IsEnabled = true;
            this.toolBar.Opacity = 1.0;
        }

        /// <summary>
        /// Fired on click of the properties button.
        /// </summary>
        /// <param name="sender">
        /// Property button.
        /// </param>
        /// <param name="e">
        /// RoutedEventArgs.
        /// </param>
        private void OnPropertiesButtonClick(object sender, RoutedEventArgs e)
        {
            this.composerRenderer.ShowProperties();
        }

        public void InitializeMachineCombo()
        {
            this.MachineList.Items.Clear();
            ComboBoxItem selectNodeText = new ComboBoxItem();
            selectNodeText.Content = TridentUtilities.Properties.ScientificWorkFlow.SelectNodeText;
            selectNodeText.Tag = TridentUtilities.Properties.ScientificWorkFlow.SelectNodeText;
            this.MachineList.Items.Add(selectNodeText);
            this.MachineList.SelectedIndex = 0;

            if (this.CurrentWorkflowModel != null && this.CurrentWorkflowModel.IsLoadedFromRegistry)
            {
                this.PopulateMachineCombo(this.CurrentWorkflowModel.SavedIsInteractiveValue);
            }
        }

        /// <summary>
        /// Populate the machine combo.
        /// </summary>
        /// <param name="setDefault">
        /// Set as default.
        /// </param>
        public void PopulateMachineCombo(bool isInteractive)
        {
            try
            {
                Collection<Machine> machines = Machine.CommonSearches.GetOnlineMachines(this.registryConnection, isInteractive);

                foreach (Machine machine in machines)
                {
                    ComboBoxItem machineItem = new ComboBoxItem();
                    machineItem.Content = machine.Name;
                    machineItem.Tag = machine;
                    this.MachineList.Items.Add(machineItem);
                }

                // If it having only one node select the node by default
                if (this.MachineList.Items.Count == 2)
                {
                    this.MachineList.SelectedIndex = 1;
                }
                else
                {
                    this.MachineList.SelectedIndex = 0;
                }
            }
            catch (RecordNotFoundException)
            {
                //Leave the list empty.
            }
            catch (Exception ex)
            {
                TridentErrorHandler.HandleUIException(ex);
                TridentMessageBox.ShowTridentErrorMessageBox(TridentResourceManager.GetString("MachineLoadError"));
            }
        }

        /// <summary>
        /// Handler for teminate button click
        /// </summary>
        /// <param name="sender">terminate button instance</param>
        /// <param name="e">event argument</param>
        private void Terminatebutton_Click(object sender, RoutedEventArgs e)
        {
            if (this.OnTerminate != null)
            {
                this.OnTerminate.Invoke(sender, new EventArgs());
            }
        }

        /// <summary>
        /// Initialize the datatype color legend pop up.
        /// </summary>
        public DataTypesLegendCtrl InitializeDataTypeWindow()
        {
            DataTypesLegendCtrl legendCtrl = this.GetDataTypesWindow(this.CurrentRenderMode) as DataTypesLegendCtrl;
            if (null == legendCtrl)
            {
                legendCtrl = new DataTypesLegendCtrl();
                legendCtrl.Owner = this.mainWindow;
                legendCtrl.Tag = "DataTypesLegendCtrl" + this.CurrentRenderMode.ToString();
                legendCtrl.ShowInTaskbar = false;
                legendCtrl.VerticalAlignment = VerticalAlignment.Top;
                legendCtrl.HorizontalAlignment = HorizontalAlignment.Right;
                legendCtrl.Top = this.mainWindow.Top + 160;
                legendCtrl.Left = this.mainWindow.Left + this.mainWindow.ActualWidth - 275;
                legendCtrl.WindowStartupLocation = WindowStartupLocation.Manual;
                legendCtrl.OnLegendClose += new EventHandler(legendCtrl_OnLegendClose);
                legendCtrl.OnControlVisibilityChanged += new DependencyPropertyChangedEventHandler(this.OnDataTypesWindowVisibilityChanged);
                legendCtrl.Hide();
            }

            this.btnDataType.IsEnabled = true;
            return legendCtrl;
        }

        /// <summary>
        /// Called when data types window visibility changes.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        private void OnDataTypesWindowVisibilityChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if ((bool)e.NewValue)
            {
                this.btnDataType.IsEnabled = false;
            }
            else
            {
                this.btnDataType.IsEnabled = true;
            }
        }

        /// <summary>
        /// Handles the OnLegendClose event of the legendCtrl control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        void legendCtrl_OnLegendClose(object sender, EventArgs e)
        {
            this.CloseDatatypeWindow();
        }

        /// <summary>
        /// Event handler for the show datatype legend click.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event arguments</param>
        private void OnShowDataTypes(object sender, RoutedEventArgs e)
        {
            Window dataTypesLegendCtrlWindow = this.GetDataTypesWindow(this.CurrentRenderMode);
            if (dataTypesLegendCtrlWindow != null)
            {
                dataTypesLegendCtrlWindow.Show();
            }
        }

        /// <summary>
        /// Gets the datatypes popup based on render mode.
        /// </summary>
        /// <param name="renderMode">
        /// Render mode.
        /// </param>
        /// <returns>
        /// The appropriate datatypes window.
        /// </returns>
        private Window GetDataTypesWindow(RenderMode renderMode)
        {
            Window datatypesWin = null;
            WindowCollection childWindows = this.mainWindow.OwnedWindows;
            foreach (Window child in childWindows)
            {
                if (child.Tag != null && child.Tag.ToString().Equals("DataTypesLegendCtrl" + renderMode.ToString()))
                {
                    datatypesWin = child;
                    break;
                }
            }

            return datatypesWin;
        }

        /// <summary>
        /// Event handler for close Workflow
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnCloseWorkFlow(object sender, RoutedEventArgs e)
        {
            if (this.OnCloseWorkflow != null)
            {
                this.OnCloseWorkflow.Invoke(sender, new EventArgs());
            }
            this.CloseDatatypeWindow();
        }

        /// <summary>
        /// Close the datatype window.
        /// </summary>
        private void CloseDatatypeWindow()
        {
            Window datatypeWindow = this.GetDataTypesWindow(this.CurrentRenderMode);
            if (datatypeWindow != null)
            {
                datatypeWindow.Hide();
            }
        }

        /// <summary>
        /// RepositionLines.
        /// </summary>
        public void RepositionLines()
        {
            this.composerRenderer.RefreshUI();
        }

        /// <summary>
        /// Refresh the Ui. 
        /// </summary>
        /// <param name="onlyBinding">
        /// Only binding.
        /// </param>
        public void RefreshUI()
        {
            this.composerRenderer.RefreshUI();
        }

        /// <summary>
        /// Recursively work on child activities.
        /// </summary>
        /// <param name="activity">
        /// Activity model.
        /// </param>
        /// <param name="parentUI">
        /// The parent ui.
        /// </param>
        /// <returns>
        /// The panel.
        /// </returns>
        public void UpdateForActivity(BaseModel model, Panel parentUI)
        {
            this.composerRenderer.UpdateForActivity(model, parentUI);
        }

        /// <summary>
        /// Handles request from the activity composer to reload the workflow. Passes up the event to the higher levels.
        /// </summary>
        /// <param workflowName="sender">
        /// Activity Composer.
        /// </param>
        /// <param workflowName="e">
        /// EventArgs.
        /// </param>
        private void OnSubsectionSwapState(object sender, EventArgs e)
        {
            this.SwapSubsectionState(sender);
        }

        #endregion Private Methods.
    }

    public class ExecuteEventArgs : EventArgs
    {
        public object SelectedMachine
        {
            get;
            set;
        }

        public string JobName
        {
            get;
            set;
        }

        public bool IsDebug
        {
            get;
            set;
        }

        public ExecuteEventArgs(string jobName, object selectedMachine, bool isDebug)
        {
            this.JobName = jobName;
            this.SelectedMachine = selectedMachine;
            this.IsDebug = isDebug;
        }
    }

    public enum RenderMode
    {
        ComposerMode,
        MonitorMode,
        ProvenanceMode,
        VisualizationMode,
        None
    }
}
